"""
Email Manager
Handles SMTP connections and email sending functionality
"""

import asyncio
import logging
from datetime import datetime
from typing import Dict, List, Any, Optional
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders
import aiosmtplib

from config import get_config

logger = logging.getLogger(__name__)

class EmailManager:
    """
    Manages email sending through SMTP with async support
    """
    
    def __init__(self):
        self.config = get_config()
        self.smtp_client = None
        self.connection_pool = []
        self.max_connections = 5
    
    async def initialize(self):
        """Initialize email manager"""
        try:
            # Test SMTP connection
            await self.check_smtp_connection()
            logger.info("Email manager initialized successfully")
        except Exception as e:
            logger.error(f"Email manager initialization failed: {e}")
            raise
    
    async def check_smtp_connection(self) -> bool:
        """Check if SMTP connection is working"""
        try:
            smtp_client = aiosmtplib.SMTP(
                hostname=self.config.email.smtp_host,
                port=self.config.email.smtp_port,
                use_tls=self.config.email.use_tls,
                timeout=10
            )
            
            await smtp_client.connect()
            
            if self.config.email.smtp_user and self.config.email.smtp_password:
                await smtp_client.login(
                    self.config.email.smtp_user,
                    self.config.email.smtp_password
                )
            
            await smtp_client.quit()
            return True
            
        except Exception as e:
            logger.error(f"SMTP connection failed: {e}")
            return False
    
    async def send_email(
        self,
        to_email: str,
        subject: str,
        html_content: str,
        text_content: Optional[str] = None,
        attachments: Optional[List[Dict[str, Any]]] = None,
        priority: str = "normal"
    ) -> Dict[str, Any]:
        """Send a single email"""
        try:
            # Create message
            message = MIMEMultipart("alternative")
            message["Subject"] = subject
            message["From"] = f"{self.config.email.from_name} <{self.config.email.from_email}>"
            message["To"] = to_email
            
            # Set priority
            if priority == "high":
                message["X-Priority"] = "1"
                message["X-MSMail-Priority"] = "High"
            elif priority == "low":
                message["X-Priority"] = "5"
                message["X-MSMail-Priority"] = "Low"
            
            # Add text content
            if text_content:
                text_part = MIMEText(text_content, "plain")
                message.attach(text_part)
            
            # Add HTML content
            html_part = MIMEText(html_content, "html")
            message.attach(html_part)
            
            # Add attachments
            if attachments:
                for attachment in attachments:
                    await self._add_attachment(message, attachment)
            
            # Send email
            await self._send_message(message, to_email)
            
            return {
                "success": True,
                "message_id": message.get("Message-ID"),
                "sent_at": datetime.utcnow().isoformat()
            }
            
        except Exception as e:
            logger.error(f"Failed to send email to {to_email}: {e}")
            return {
                "success": False,
                "error": str(e)
            }
    
    async def send_bulk_emails(
        self,
        recipients: List[Dict[str, Any]],
        subject_template: str,
        html_template: str,
        text_template: Optional[str] = None,
        batch_size: int = 50
    ) -> Dict[str, Any]:
        """Send bulk emails with personalization"""
        try:
            total_sent = 0
            total_failed = 0
            errors = []
            
            # Process in batches
            for i in range(0, len(recipients), batch_size):
                batch = recipients[i:i + batch_size]
                
                # Send batch
                batch_results = await self._send_email_batch(
                    batch, subject_template, html_template, text_template
                )
                
                total_sent += batch_results["sent"]
                total_failed += batch_results["failed"]
                errors.extend(batch_results["errors"])
                
                # Rate limiting - small delay between batches
                if i + batch_size < len(recipients):
                    await asyncio.sleep(1)
            
            return {
                "success": True,
                "total_recipients": len(recipients),
                "sent": total_sent,
                "failed": total_failed,
                "errors": errors
            }
            
        except Exception as e:
            logger.error(f"Bulk email sending failed: {e}")
            return {
                "success": False,
                "error": str(e)
            }
    
    async def _send_email_batch(
        self,
        batch: List[Dict[str, Any]],
        subject_template: str,
        html_template: str,
        text_template: Optional[str] = None
    ) -> Dict[str, Any]:
        """Send a batch of emails"""
        sent = 0
        failed = 0
        errors = []
        
        # Create concurrent tasks for the batch
        tasks = []
        
        for recipient_data in batch:
            task = self._send_personalized_email(
                recipient_data,
                subject_template,
                html_template,
                text_template
            )
            tasks.append(task)
        
        # Execute batch concurrently
        results = await asyncio.gather(*tasks, return_exceptions=True)
        
        for i, result in enumerate(results):
            if isinstance(result, Exception):
                failed += 1
                errors.append({
                    "recipient": batch[i].get("email", "unknown"),
                    "error": str(result)
                })
            elif result.get("success"):
                sent += 1
            else:
                failed += 1
                errors.append({
                    "recipient": batch[i].get("email", "unknown"),
                    "error": result.get("error", "Unknown error")
                })
        
        return {
            "sent": sent,
            "failed": failed,
            "errors": errors
        }
    
    async def _send_personalized_email(
        self,
        recipient_data: Dict[str, Any],
        subject_template: str,
        html_template: str,
        text_template: Optional[str] = None
    ) -> Dict[str, Any]:
        """Send personalized email to single recipient"""
        try:
            # Extract recipient email
            to_email = recipient_data.get("email")
            if not to_email:
                raise ValueError("Recipient email not provided")
            
            # Personalize templates
            subject = self._personalize_template(subject_template, recipient_data)
            html_content = self._personalize_template(html_template, recipient_data)
            text_content = None
            
            if text_template:
                text_content = self._personalize_template(text_template, recipient_data)
            
            # Send email
            return await self.send_email(
                to_email=to_email,
                subject=subject,
                html_content=html_content,
                text_content=text_content
            )
            
        except Exception as e:
            logger.error(f"Personalized email sending failed: {e}")
            return {
                "success": False,
                "error": str(e)
            }
    
    def _personalize_template(self, template: str, data: Dict[str, Any]) -> str:
        """Replace template variables with actual data"""
        try:
            # Simple template variable replacement
            personalized = template
            
            for key, value in data.items():
                placeholder = "{{" + key + "}}"
                personalized = personalized.replace(placeholder, str(value))
            
            return personalized
            
        except Exception as e:
            logger.error(f"Template personalization failed: {e}")
            return template
    
    async def _add_attachment(self, message: MIMEMultipart, attachment: Dict[str, Any]):
        """Add attachment to email message"""
        try:
            if attachment.get("content"):
                # Attachment provided as content
                part = MIMEBase("application", "octet-stream")
                part.set_payload(attachment["content"])
                encoders.encode_base64(part)
                
                part.add_header(
                    "Content-Disposition",
                    f"attachment; filename= {attachment.get('filename', 'attachment')}"
                )
                message.attach(part)
            
            elif attachment.get("filepath"):
                # Attachment provided as file path
                with open(attachment["filepath"], "rb") as f:
                    part = MIMEBase("application", "octet-stream")
                    part.set_payload(f.read())
                    encoders.encode_base64(part)
                    
                    filename = attachment.get("filename") or attachment["filepath"].split("/")[-1]
                    part.add_header(
                        "Content-Disposition",
                        f"attachment; filename= {filename}"
                    )
                    message.attach(part)
                    
        except Exception as e:
            logger.error(f"Failed to add attachment: {e}")
    
    async def _send_message(self, message: MIMEMultipart, recipient: str):
        """Send the constructed message"""
        try:
            smtp_client = aiosmtplib.SMTP(
                hostname=self.config.email.smtp_host,
                port=self.config.email.smtp_port,
                use_tls=self.config.email.use_tls,
                timeout=30
            )
            
            await smtp_client.connect()
            
            if self.config.email.smtp_user and self.config.email.smtp_password:
                await smtp_client.login(
                    self.config.email.smtp_user,
                    self.config.email.smtp_password
                )
            
            # Send message
            await smtp_client.send_message(message)
            await smtp_client.quit()
            
            logger.debug(f"Email sent successfully to {recipient}")
            
        except Exception as e:
            logger.error(f"SMTP sending failed for {recipient}: {e}")
            raise
    
    async def get_email_stats(self) -> Dict[str, Any]:
        """Get email sending statistics"""
        try:
            # This would typically query a database for email logs
            # For now, return basic stats
            return {
                "smtp_configured": bool(self.config.email.smtp_host),
                "from_email": self.config.email.from_email,
                "smtp_host": self.config.email.smtp_host,
                "smtp_port": self.config.email.smtp_port,
                "use_tls": self.config.email.use_tls
            }
        except Exception as e:
            logger.error(f"Failed to get email stats: {e}")
            return {}
    
    async def validate_email_address(self, email: str) -> bool:
        """Validate email address format and deliverability"""
        try:
            import re
            
            # Basic email format validation
            email_pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
            if not re.match(email_pattern, email):
                return False
            
            # Could add more sophisticated validation here
            # such as DNS MX record checking
            
            return True
            
        except Exception as e:
            logger.error(f"Email validation failed for {email}: {e}")
            return False
    
    async def test_email_configuration(self) -> Dict[str, Any]:
        """Test email configuration and connectivity"""
        try:
            test_results = {
                "smtp_connection": False,
                "authentication": False,
                "configuration": {
                    "smtp_host": self.config.email.smtp_host,
                    "smtp_port": self.config.email.smtp_port,
                    "use_tls": self.config.email.use_tls,
                    "from_email": self.config.email.from_email
                },
                "errors": []
            }
            
            # Test SMTP connection
            try:
                smtp_client = aiosmtplib.SMTP(
                    hostname=self.config.email.smtp_host,
                    port=self.config.email.smtp_port,
                    use_tls=self.config.email.use_tls,
                    timeout=10
                )
                
                await smtp_client.connect()
                test_results["smtp_connection"] = True
                
                # Test authentication
                if self.config.email.smtp_user and self.config.email.smtp_password:
                    await smtp_client.login(
                        self.config.email.smtp_user,
                        self.config.email.smtp_password
                    )
                    test_results["authentication"] = True
                
                await smtp_client.quit()
                
            except Exception as e:
                test_results["errors"].append(f"SMTP connection/auth failed: {str(e)}")
            
            return test_results
            
        except Exception as e:
            logger.error(f"Email configuration test failed: {e}")
            return {
                "smtp_connection": False,
                "authentication": False,
                "errors": [str(e)]
            }